// Copyright (c) 2020-present,  INSPUR Co, Ltd.  All rights reserved.
// This source code is licensed under Apache 2.0 License.
////改好后修改
#include "db/db_test_util.h"
#include "memory_arena_art_index.h"
#include "memory_arena_vector_index.h"
#include "pure_mem/pmemrep.h"
#include "pure_mem/rangearena/range_arena.h"
#include "util/random.h"
#include "util/testharness.h"

namespace rocksdb{
    class TestKeyComparator : public MemTableRep::KeyComparator {
    public:
        typedef rocksdb::Slice DecodedType;

        DecodedType decode_key(const char* key) const override {
            // The format of key is frozen and can be terated as a part of the API
            // contract. Refer to MemTable::Add for details.
            return GetLengthPrefixedSlice(key);
        }

        // Compare a and b. Return a negative value if a is less than b, 0 if they
        // are equal, and a positive value if a is greater than b
        // Compare size in byte order
        int operator()(const char* prefix_len_key1,
                       const char* prefix_len_key2) const override {
            Slice k1 = GetLengthPrefixedSlice(prefix_len_key1);
            Slice k2 = GetLengthPrefixedSlice(prefix_len_key2);

            int comLength = std::min(k1.size(), k2.size());
            for (int i = 0; i < comLength; i++) {
                if ((uint8_t)k1[i] > (uint8_t)k2[i]) {
                    return 1;
                } else if ((uint8_t)k1[i] < (uint8_t)k2[i])
                    return -1;
            }

            if (k1.size() > k2.size()) return 1;
            if (k1.size() < k2.size()) return -1;
            return 0;
        }

        int operator()(const char* prefix_len_key, const Slice& key) const override {
            Slice key_pre = GetLengthPrefixedSlice(prefix_len_key);
            return key_pre.compare(key);
        }

        ~TestKeyComparator() override = default;
    };

    typedef uint64_t Key;
    typedef PureMemRep TestMemArenaRep;
    const size_t standard_key = 1000000;

    static const char* Encode(const Key* key) {
        std::string str = std::to_string(*key);
        int len = (int)(str.length()) + 8 + 1;
        uint32_t keySize = VarintLength(len) + len;
        char* ret = new char[keySize];
        memset(ret , '\0', keySize);
        char*p = EncodeVarint32(ret, len);
        memcpy(p, str.c_str(), str.length());
        return reinterpret_cast<const char*>(ret);
    }

    static Slice Decode(const char* key) {
        Slice buf = GetLengthPrefixedSlice(key);
        Slice mvccKey = Slice(buf.data(), buf.size() - 8);
        const char ts_size = mvccKey[mvccKey.size() - 1];
        if ((size_t)ts_size >= mvccKey.size()) {
            return nullptr;
        }
        return {buf.data(), mvccKey.size() - 1 - ts_size};
    }


    class MemArenaVectorIndexTest : public testing::Test {
    public:
        static void Insert(TestMemArenaRep* list, Key key) {
            char** buf = nullptr;
            void* rangeArena;
            const auto& keyy = (const Slice&) key;
            const size_t len = sizeof(Key);
            *buf = (char*)list->AllocatePure(len, buf, keyy, &rangeArena);
            memcpy(*buf, &key, sizeof(Key));
            void* buff = *buf;
            list->Insert(buff);
        }

        void Validate(TestMemArenaRep* list) {
            // Check keys exist.
            for (Key key : keys_) {
                ASSERT_TRUE(list->Contains(Encode(&key)));
            }
            // Iterate over the list, make sure keys appears in order and no extra
            // keys exist.
            InlineUserKeyIndex<const MemTableRep::KeyComparator&>::Iterator iter =
                    (InlineUserKeyIndex<const MemTableRep::KeyComparator&>::Iterator)
                            reinterpret_cast<const InlineUserKeyIndex<const MemTableRep::KeyComparator &> *>(list->GetIterator());
            ASSERT_FALSE(iter.Valid());
            Key zero = 0;
            iter.Seek(Encode(&zero));
            for (Key key : keys_) {
                ASSERT_TRUE(iter.Valid());
                ASSERT_EQ((char*)key, Decode(iter.key()).data());
                iter.Next();
            }
            ASSERT_FALSE(iter.Valid());
        }

    private:
        std::set<Key> keys_;
    };

    Slice genMVCCKey1(const char* key) {
        uint32_t keySize = strlen(key) + 1;
        char* ret = new char[keySize];
        memset(ret, '\0', keySize);
        memcpy(ret, key, strlen(key));
        return Slice(ret, keySize);
    }


    Slice GenInternalKey(const Slice &key, char *buf){
        size_t key_size = key.size() + 1;
        size_t internal_key_size = key_size + 8;

        char* p = buf;
        memset(p, '\0', key_size);
        memcpy(p, key.data(), key.size());
        p += key_size;
        memset(p, 255, 8);
        return Slice(buf, internal_key_size);
    }


    //测试第一层索引为空的Seek
    TEST_F(MemArenaVectorIndexTest, SeekForEmptyAndTest) {

        InternalKeyComparator comparator(BytewiseComparator());
        Options options = Options();
        ImmutableCFOptions ioptions(options);

        int bound = 1;
        auto manager = new MemArenaVectorIndex();
        MemArena *memoryArenas[4];
        std::string uk[4];
        Slice userkeys[4];
        char* buf[4];
        size_t encode_len = 0;

        //将startkey和MemoryArena插入第一层ART索引
        for (int i = 0; i < 4; i++) {
            memoryArenas[i] = new MemArena(comparator, ioptions);
            uk[i] = std::to_string(bound + 2 * i);
            userkeys[i] = Slice(uk[i].data(), uk[i].size());

            encode_len = uk[i].size() + 1 + 8;
            buf[i] = new char[encode_len];
            Slice internal_key = Slice(GenInternalKey(userkeys[i], buf[i]));
            auto memory = manager->SeekForMemoryArena(internal_key);
            if(memory != nullptr){
                std::cout<<"查到的memoryarena为："<<(void*)memory<<std::endl;
                exit(1);
            }
        }
        delete manager;
        for(int i = 0; i < 4; i++){
            delete[] buf[i];
            delete memoryArenas[i];
        }
    }

}
int main(int argc, char** argv) {
    ::testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}